home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-06-15 | 60.3 KB | 1,861 lines |
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 26
- #HS,1,4,80,25,11,1
- #C4,R5
- ~W~IImplementing Attackers~Y~I
-
- Now we have enough to write the code for an attacker, after which, we'll
- write enough of the game to get the attacker functioning. The header file
- ~G~IATTACKER.H~Y~I will contain the type definition, macros, and prototypes we'll
- need for the attacker. Let's look at that first.
-
- #WN
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 27
- #HS,1,4,80,25,11,1
- #C1,R5
- ~W~I Listing 8.5~Y~I
- ATTACKER.H
-
- /*------------------------------attacker.h-----------------------------------*/
- /*
- Copyright 1992 David Conger
- */
-
- #ifndef __ATTACKER_H__
-
- /*-----------------------------include files--------------------------------*/
-
- ~W~I #include "game.h"
- #include "charactr.h"~Y~I
-
- /*---------------------------end include files------------------------------*/
-
- #C1,R23
- ~C~IContinued On Next Page~Y~I
-
- #BO,4,7,78,11,7,1,0,3,0,7
- The attacker makes use of the types boolean and character. These are
- defined in GAME.H and CHARACTR.H respectively, so both files are
- included in ATTACKER.H.
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 28
- #HS,1,4,80,25,11,1
- #C1,R5
- ~W~I Listing 8.5 (cont)~Y~I
- ATTACKER.H
-
-
-
- /*----------------------------type definitions------------------------------*/
-
- ~W~I typedef enum {ATTACKER_LEFT,ATTACKER_RIGHT,ATTACKER_DOWN} attacker_direction;~Y~I
-
- typedef struct
- {
- attacker_direction current_direction;
- character critter;
- boolean at_edge;
- } attacker;
-
- /*--------------------------end type definitions----------------------------*/
-
- #C1,R23
- ~C~IContinued On Next Page~Y~I
-
- #BO,4,17,78,23,7,1,0,5,0,7
- An attacker, like a player, has a direction that it is moving in at
- all times. Unlike a player, an attacker may never stand still and it
- may move down. It is for this reason that a different enumerated type,
- attacker_direction, was defined for attackers rather than reuse the
- same one that was used for a player in Listing 8.1.
-
- #C1,R10
- ~Y~I /*----------------------------type definitions------------------------------*/
-
- typedef enum {ATTACKER_LEFT,ATTACKER_RIGHT,ATTACKER_DOWN} attacker_direction;
-
- ~W~I typedef struct
- {
- attacker_direction current_direction;
- character critter;
- boolean at_edge;
- } attacker;~Y~I
-
- /*--------------------------end type definitions----------------------------*/
-
-
- #BO,4,5,78,11,7,1,0,5,0,7
- The attacker structure is just as we saw it in the last chapter.
- Remember that the only attackers that can fire their bullets are
- those which have no other attacker immediately below them. The
- member at_edge, tells us whether or not a particular attacker is in
- a position to fire.
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 29
- #HS,1,4,80,25,11,1
- #C1,R5
- ~W~I Listing 8.5 (cont)~Y~I
- ATTACKER.H
-
- /*--------------------------------macros-----------------------------------*/
-
- #define set_attacker_col(atkr,atkrcol) \
- set_character_col(atkr->critter,atkrcol)
-
- #define set_attacker_row(atkr,atkrrow) \
- set_character_row(atkr->critter,atkrrow)
-
- #define set_attacker_position(atkr,atkr_row,atkr_col) \
- set_character_position(atkr->critter,atkr_row,atkr_col)
-
- #define set_attacker_direction(atkr,atkr_dir) \
- atkr->current_direction=atkr_dir
-
- #C1,R23
- ~C~IContinued On Next Page~Y~I
-
- #BO,40,10,78,17,7,1,0,6,0,7
- The next few pages of Listing 8.5
- contain the macros that implement
- many of the operations that can be
- performed on an attacker. The
- majority of these are just calls to
- character macros and point macros.
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 30
- #HS,1,4,80,25,11,1
- #C1,R5
- ~W~I Listing 8.5 (cont)~Y~I
- ATTACKER.H
-
- #define set_attacker_at_edge(atkr,t_f) atkr->at_edge=t_f
-
- #define set_attacker_bullet_color(atkr,bcolor) \
- set_character_bullet_color(atkr->critter,bcolor)
-
- #define set_attacker_bullet_direction(atkr,adir) \
- set_character_bullet_direction(atkr->critter,adir)
-
- #define set_attacker_bullet(atkr,t_f) \
- set_character_bullet(atkr->critter,t_f)
-
- #define set_attacker_bullet_row(atkr,br) \
- set_character_bullet_row(atkr->critter,(br))
-
-
- #C1,R23
- ~C~IContinued On Next Page~Y~I
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 31
- #HS,1,4,80,25,11,1
- #C1,R5
- ~W~I Listing 8.5 (cont)~Y~I
- ATTACKER.H
-
- #define set_attacker_bullet_col(atkr,bc) \
- set_character_bullet_col(atkr->critter,(bc))
-
- #define set_attacker_bitmap(atkr,bmap) \
- set_character_bitmap(atkr->critter,bmap)
-
- #define set_attacker_bitmap_height(atkr,ahi) \
- set_character_bitmap_height(atkr->critter,ahi)
-
- #define set_attacker_bitmap_width(atkr,awi) \
- set_character_bitmap_width(atkr->critter,awi)
-
-
- #C1,R23
- ~C~IContinued On Next Page~Y~I
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 32
- #HS,1,4,80,25,11,1
- #C1,R5
- ~W~I Listing 8.5 (cont)~Y~I
- ATTACKER.H
-
- #define set_attacker_is_alive(atkr,t_f) \
- set_character_is_alive(atkr->critter,t_f)
-
- #define set_attacker_move_increment(atkr,mi) \
- set_character_move_increment(atkr->critter,mi)
-
- #define get_attacker_row(atkr) \
- get_character_row(atkr->critter)
-
- #define get_attacker_col(atkr) \
- get_character_col(atkr->critter)
-
- #C1,R23
- ~C~IContinued On Next Page~Y~I
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 33
- #HS,1,4,80,25,11,1
- #C1,R5
- ~W~I Listing 8.5 (cont)~Y~I
- ATTACKER.H
-
- #define get_attacker_move_increment(atkr) \
- get_character_move_increment(atkr->critter)
-
- #define get_attacker_bullet(atkr) get_character_bullet(atkr->critter)
-
- #define get_attacker_bullet_row(atkr) \
- get_character_bullet_row(atkr->critter)
-
- #define get_attacker_bullet_col(atkr) \
- get_character_bullet_col(atkr->critter)
-
- #C1,R23
- ~C~IContinued On Next Page~Y~I
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 34
- #HS,1,4,80,25,11,1
- #C1,R5
- ~W~I Listing 8.5 (cont)~Y~I
- ATTACKER.H
-
- #define get_attacker_has_bullet(atkr) \
- get_character_has_bullet(atkr->critter)
-
- #define get_attacker_bullet_color(atkr) \
- get_character_bullet_color(atkr->critter)
-
- #define get_attacker_bitmap(atkr) \
- get_character_bitmap(atkr->critter)
-
- #define get_attacker_bitmap_height(atkr) \
- get_character_bitmap_height(atkr->critter)
-
- #define get_attacker_bitmap_width(atkr) \
- get_character_bitmap_width(atkr->critter)
-
- #C1,R23
- ~C~IContinued On Next Page~Y~I
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 35
- #HS,1,4,80,25,11,1
- #C1,R5
- ~W~I Listing 8.5 (cont)~Y~I
- ATTACKER.H
-
-
- #define get_attacker_is_alive(atkr) \
- get_character_is_alive(atkr->critter)
-
- #define get_attacker_direction(atkr) atkr->current_direction
-
- #define get_attacker_at_edge(atkr) atkr->at_edge
-
- /*------------------------------end macros---------------------------------*/
-
-
- #C1,R23
- ~C~IContinued On Next Page~Y~I
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 36
- #HS,1,4,80,25,11,1
- #C1,R5
- ~W~I Listing 8.5 (cont)~Y~I
- ATTACKER.H
-
- /*------------------------------Prototypes---------------------------------*/
-
- void init_attackers(attacker *badie,int rows_of_attackers,
- int cols_of_attackers,
- int init_up_left_row,int init_up_left_col,
- int distance_between_rows,int
- distance_between_attackers,
- attacker_direction init_direction,int
- move_increment,
- bitmap image,int image_height,int image_width);
-
- void draw_attackers(attacker *meanies,
- int rows_of_attackers,
- int cols_of_attackers);
-
- #C1,R23
- ~C~IContinued On Next Page~Y~I
-
- #BO,4,17,78,20,7,1,0,2,0,7
- The rest of the operations that can be performed on an attacker are
- found in the functions whose prototypes at the end of Listing 8.5.
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 37
- #HS,1,4,80,25,11,1
- #C1,R5
- ~W~I Listing 8.5 (cont)~Y~I
- ATTACKER.H
-
- void shoot_attacker_gun(attacker *nasties,
- int rows_of_attackers,
- int cols_of_attackers);
-
- boolean move_attacker_row(attacker *nasties,int rows_of_attackers,
- int cols_of_attackers,
- int *current_row);
-
- boolean attackers_all_dead(attacker *thugs,
- int rows_of_attackers,
- int cols_of_attackers);
-
- void draw_an_attacker(attacker *thug,int operation);
-
- #C1,R23
- ~C~IContinued On Next Page~Y~I
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 38
- #HS,1,4,80,25,11,1
- #C1,R5
- ~W~I Listing 8.5 (cont)~Y~I
- ATTACKER.H
-
- void erase_an_attacker(attacker *thug);
-
- /*----------------------------End prototypes-------------------------------*/
-
-
- #define __ATTACKER_H__
- #endif
-
- /*------------------------------attacker.h-----------------------------------*/
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 39
- #HS,1,4,80,25,11,1
- #C4,R5
- ~Y~I
- One of the drawbacks with accessing several nested structures through
- macro interfaces is very apparent in ~G~IATTACKER.H~Y~I. Notice that, since an
- attacker has a character in it, macros must be provided that give access
- to the fields in the character member. These macros almost duplicate the
- macros in ~G~ICHARACTR.H~Y~I. The character structure, in turn, contains a point.
- In order to provide access to the members of the point that are in the
- character, we've got to produce more macros.
-
- #WN
- The upshot of all of this is that using this type of interface is forcing
- us to write a lot of macros. In an object oriented language that supports
- inheritance like C++, this would not be the case. However, in C we can't
- avoid this problem if we want to gain the kind of flexibility and
- maintainability that this style of programming provides.
-
- #WN
- The functions that perform the more complex operations on an attacker are
- contained in ~G~IATTACKER.C~Y~I, which is shown in Listing 8.6.
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 40
- #HS,1,4,80,25,11,1
- #C1,R5
- ~W~IListing 8.6~Y~I
- ATTACKER.C
-
- /*-----------------------------attacker.c-----------------------------------*/
- /*
- Copyright 1992 David Conger
- */
-
- /*-----------------------------include files--------------------------------*/
-
- ~W~I #include "game.h"
- #include "attacker.h"
- #include "bullet.h"
- #include "charactr.h"
- #include <graphics.h>
- #include "noises.h"~Y~I
-
- /*---------------------------end include files------------------------------*/
-
-
-
- #C1,R23
- ~C~IContinued On Next Page~Y~I
-
- #BO,4,7,78,10,7,1,0,2,0,7
- ATTACKER.C starts off by including the header files that are necessary
- to its operation.
-
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 41
- #HS,1,4,80,25,11,1
- #C1,R5
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
-
- /*----------------------------local prototypes------------------------------*/
-
- ~W~I static void set_all_attacker_direction(attacker *thugs,int rows_of_attackers,
- int cols_of_attackers,
- attacker_direction dir);
- ~Y~I
- /*--------------------------end local prototypes----------------------------*/
-
-
- #C1,R23
- ~C~IContinued On Next Page~Y~I
-
-
- #BO,4,17,78,22,7,1,0,4,0,7
- The next step is to declare the prototype for a static function. This
- function is used only in this file so it is made static. This is in
- keeping with the ideas of information and implementation hiding that
- we discussed in Chapter 5.
-
- #BO,4,17,78,22,7,1,0,4,0,7
- I often tell my students that we need to hide things better than the
- CIA. We should only make information and implementation available on a
- "need to know" basis. If a function is only used in a single C source
- file, it should be static so that it is visible only in that file.
-
- #BO,4,17,78,20,7,1,0,2,0,7
- If it is needed by more than one file, then the storage class should
- not be static, and its prototype should be put in a header file.
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 42
- #HS,1,4,80,25,11,1
- #C1,R5
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
-
- /*-----------------------------init_attackers-------------------------------*/
-
- void init_attackers(attacker *badie,int rows_of_attackers,
- int cols_of_attackers,
- int init_up_left_row,int init_up_left_col,
- int distance_between_rows,
- int distance_between_attackers,
- attacker_direction init_direction,int move_increment,
- bitmap image,int image_height,int image_width)
- {
- int i,j;
- attacker *temp;
-
- #C1,R23
- ~C~IContinued On Next Page~Y~I
-
- #BO,4,17,78,20,7,1,0,2,0,7
- The first function in Listing 8.6 is init_attackers(). This function
- initializes the fields in a two dimensional array of attackers.
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 43
- #HS,1,4,80,25,11,1
- #C1,R4
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
- for (i=0;i<rows_of_attackers;i++)
- {
- for(j=0;j<cols_of_attackers;j++)
- {
- temp=badie + (i*cols_of_attackers) + j;
- set_attacker_bullet(temp,FALSE);
- set_attacker_is_alive(temp,TRUE);
- set_attacker_bitmap(temp,image);
- set_attacker_row(temp,
- init_up_left_row+(i*distance_between_rows));
- set_attacker_col(temp,
- init_up_left_col+(j*distance_between_attackers));
- set_attacker_direction(temp,init_direction);
- set_attacker_move_increment(temp,move_increment);
- set_attacker_bitmap_height(temp,image_height);
- set_attacker_bitmap_width(temp,image_width);
- set_attacker_at_edge(temp,FALSE);
- }
- }
-
- #BO,4,17,78,23,7,1,0,5,0,7
- These lines show a pair of nested for loops that go through the 2D
- array row by row and column by column, initializing the structures
- with the parameter values that were passed to the function. As
- always, we are using the macro interface to store values into the
- structures rather than refer to the structure members directly.
-
-
- #C1,R10
- ~W~I temp=badie + (i*cols_of_attackers) + j;~Y~I
-
- #BO,4,17,78,20,7,1,0,2,0,7
- If you're not familiar with using pointer notation to index into a 2D
- array, this line can look a little odd.
-
-
- #BO,4,16,78,23,7,1,0,6,0,7
- This instruction sets a pointer called "temp" to point at each attacker
- in the array. The variable "i" is the current row number, "j" is the
- column number. By multiplying "i" times the width of a row (the width
- of a row is the number of columns per row), we index our pointer to the
- beginning of each row in the array. By adding the current column number,
- we can access each attacker in the current row one by one.
-
- #C1,R10
- ~Y~I temp=badie + (i*cols_of_attackers) + j;
- ~W~I set_attacker_bullet(temp,FALSE);~Y~I
-
- #BO,4,17,78,21,7,1,0,3,0,7
- There are only three fields in each structure that are not set to values
- that are passed into this function as parameters. The first of these
- fields tell the attacker that it doesn't have a bullet.
-
- #C1,R11
- ~Y~I set_attacker_bullet(temp,FALSE);
- ~W~I set_attacker_is_alive(temp,TRUE);~Y~I
-
- #BO,4,17,78,19,7,1,0,1,0,7
- Here we tell the attacker that it is alive.
-
- #C1,R12
- ~Y~I set_attacker_is_alive(temp,TRUE);
-
- #C1,R22
- ~W~I set_attacker_at_edge(temp,FALSE);~Y~I
-
- #BO,4,7,78,12,7,1,0,4,0,7
- This tells the attacker that it is on the edge of its column. By
- default, the attacker is assumed at initialization to have no bullet,
- to be alive, and to be somewhere other than at the edge of its
- column.
-
- #BO,4,7,78,11,7,1,0,3,0,7
- The attackers that actually are at the edge, however, need to be told
- otherwise. Attackers are at the edge of their columns when where is no
- attacker immediately below them.
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 44
- #HS,1,4,80,25,11,1
- #C1,R5
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
-
- temp=badie+((rows_of_attackers-1)*cols_of_attackers);
- for (i=0;i<cols_of_attackers;i++,temp++)
- set_attacker_at_edge(temp,TRUE);
- }
-
- /*---------------------------end init_attackers-----------------------------*/
-
-
- #C1,R23
- ~C~IContinued On Next Page~Y~I
-
- #BO,4,17,78,21,7,1,0,3,0,7
- Initially, the only attackers that are at the edge of their columns are
- those in the bottom row. These are the ones that must be told they can
- fire.
-
- #C1,R5
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
-
- ~W~I temp=badie+((rows_of_attackers-1)*cols_of_attackers);~Y~I~N
-
- #BO,4,17,78,20,7,1,0,2,0,7
- First, the pointer "temp" needs to be pointed at the beginning of the
- bottom row of attackers.
-
- #C1,R5
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
-
- temp=badie+((rows_of_attackers-1)*cols_of_attackers);
- ~W~I for (i=0;i<cols_of_attackers;i++,temp++)
- set_attacker_at_edge(temp,TRUE);~Y~I
-
- #BO,4,17,78,20,7,1,0,2,0,7
- Next, we go into a loop that marches down the row and tells each
- attacker in that row that it is on bottom.
-
- #BO,4,17,78,20,7,1,0,2,0,7
- Once this loop is complete,the no more initializations need to be
- done, so the function returns.
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 45
- #HS,1,4,80,25,11,1
- #C1,R4
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
- /*----------------------------draw_attackers--------------------------------*/
- void draw_attackers(attacker *meanies,int rows_of_attackers,
- int cols_of_attackers)
- {
- int i,j;
- attacker *temp=meanies;
- for (i=0;i<rows_of_attackers;i++)
- {
- for (j=0;j<cols_of_attackers;j++)
- {
- if (get_attacker_is_alive(temp))
- draw_an_attacker(temp,COPY_PUT);
- temp++;
- }
- }
- }
- /*--------------------------end draw_attackers------------------------------*/
-
- #C1,R24
- ~C~IContinued On Next Page~Y~I
-
- #BO,4,17,78,21,7,1,0,3,0,7
- The next function in Listing 8.6 is draw_attackers(). The purpose of
- this function is to be able to draw the entire array of attackers with
- one function call. It takes three parameters.
-
- #C1,R7
- void draw_attackers(~W~Iattacker *meanies~Y~I,int rows_of_attackers,
- int cols_of_attackers)
-
- #BO,4,17,78,19,7,1,0,1,0,7
- The first is a pointer to the array of attackers.
-
- #C1,R7
- void draw_attackers(attacker *meanies,~W~Iint rows_of_attackers~Y~I,
- ~W~Iint cols_of_attackers~Y~I)
-
- #BO,4,17,78,20,7,1,0,2,0,7
- The second and third parameters are the number of rows and columns in
- the array respectively.
-
- #BO,4,14,78,23,7,1,0,8,0,7
- We could, if we wanted to, specify the size of the array with named
- constants in the declaration of the first parameter. If we did, the
- prototype for this function would look like what is shown below.
-
- void draw_attackers(
- attacker meanies[ROWS_OF_ATTACKERS][COLS_OF_ATTACKERS]);
-
- By doing this, we have eliminated two parameters.
-
-
- #BO,4,15,78,23,7,1,0,7,0,7
- That's ok if we're always going to have the same number of rows and
- columns of attackers (that's what we're doing in this game). However,
- one of the possible improvements you might want to make to the game is
- to have a few different arrays of attackers, each of different size.
- At lower levels of play, there would probably be fewer attackers. As
- the user moved to higher levels in the game, he/she could be presented
- with more attackers on the screen to shoot.
-
- #BO,4,17,78,22,7,1,0,4,0,7
- If we had written the code for a fixed sized array, it would have made
- this enhancement harder to do. Remember, maintenance (which includes
- enhancements) is the most costly part of the software life cycle.
- Anything we can do to plan ahead will help.
-
- #C1,R7
- void draw_attackers(attacker *meanies,int rows_of_attackers,
- int cols_of_attackers)
- {
- int i,j;
- ~W~Iattacker *temp=meanies~Y~I;
-
- #BO,4,17,78,21,7,1,0,3,0,7
- The draw_attackers() function is fairly simple. On this line, a
- temporary working pointer is created and set to point at the
- beginning of the array of attackers.
-
- #C1,R11
- attacker *temp=meanies;
- ~W~I for (i=0;i<rows_of_attackers;i++)
- {
- for (j=0;j<cols_of_attackers;j++)
- {
- if (get_attacker_is_alive(temp))
- draw_an_attacker(temp,COPY_PUT);
- temp++;
- }
- }~Y~I
-
- #BO,4,4,78,10,7,1,0,5,0,7
- These two nested loops step through the array, attacker by attacker.
- Each attacker is asked if it is alive. If it is, it is drawn. If it
- isn't, it is skipped. Video games are the only place you can ask
- someone if they're alive, get a negative answer, and have it be
- accurate.
-
- #C1,R12
- ~Y~I for (i=0;i<rows_of_attackers;i++)
- {
- for (j=0;j<cols_of_attackers;j++)
- {
- if (get_attacker_is_alive(temp))
- ~W~I draw_an_attacker(temp,COPY_PUT);~Y~I
- temp++;
- }
- }
-
- #BO,4,7,78,9,7,1,0,1,0,7
- Each attacker is drawn by calling the function draw_an_attacker().
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 46
- #HS,1,4,80,25,11,1
- #C1,R5
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
-
-
- /*---------------------------draw_an_attacker-------------------------------*/
-
- void draw_an_attacker(attacker *thug,int operation)
- {
- putimage(get_attacker_col(thug),get_attacker_row(thug),
- get_attacker_bitmap(thug),operation);
- }
-
- /*-------------------------end draw_an_attacker-----------------------------*/
-
-
- #C1,R23
- ~C~IContinued On Next Page~Y~I
-
- #BO,4,14,78,23,7,1,0,8,0,7
- This function is just a call to the BGI graphics library function
- putimage(). Usually, I would have made this function to be of
- storage class static, since it is called only by functions in this
- file. However, it occurred to me that, as an enhancement, you might
- want to make a rogue attacker - one that wanders around the screen
- independently of the array of attackers. This would be a great
- challenge for the user, especially at higher levels of play.
- Therefore, I didn't make the function static.
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 47
- #HS,1,4,80,25,11,1
- #C1,R5
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
-
- /*-----------------------------move_attacker_row----------------------------*/
-
- boolean move_attacker_row(attacker *nasties,int rows_of_attackers,
- int cols_of_attackers,
- int *current_row)
- {
- int i,j;
- attacker *temp=nasties+(*current_row * cols_of_attackers);
- static attacker_direction go_to=ATTACKER_RIGHT;
- static int maxy=0;
- boolean still_in_play=TRUE;
-
-
- #C1,R23
- ~C~IContinued On Next Page~Y~I
-
- #BO,4,17,78,20,7,1,0,2,0,7
- The function move_attacker_row() is next. The purpose of the function
- is to move a row of attackers in the appropriate direction.
-
- #C1,R10
- ~W~Iboolean ~Y~Imove_attacker_row(attacker *nasties,int rows_of_attackers,
-
- #BO,4,17,78,23,7,1,0,5,0,7
- The return type of this function is boolean. If the row, or any
- attacker in the row, moves out of play, this function returns FALSE.
- If it is still in play, the value TRUE will be returned. A row of
- attackers moves out of play when it gets to the bottom of the screen.
- The game should then terminate.
-
- #C1,R10
- boolean move_attacker_row(~W~Iattacker *nasties~Y~I,~W~Iint rows_of_attackers~Y~I,
- ~W~Iint cols_of_attackers~Y~I,
- ~W~Iint *current_row~Y~I)
-
- #BO,4,17,78,22,7,1,0,4,0,7
- The parameters that move_attacker_row() expects are a pointer to the
- array of attackers, the number of rows in the array, the number of
- columns in the array, and a pointer to an integer that keeps track of
- the row being moved.
-
- #C1,R10
- ~Y~Iboolean move_attacker_row(attacker *nasties,int rows_of_attackers,
- int cols_of_attackers,
- ~W~Iint *current_row~Y~I)
-
- #BO,4,17,78,23,7,1,0,5,0,7
- The reason that the last parameter is a pointer to an int rather
- than just an int is that this function will keep track of which row
- should be moved next. When it reaches the last row of attackers, it
- starts over. Since we need to be able to change the integer, we need
- a pointer to it.
-
- #C1,R12
- ~Y~Iint *current_row)
- {
- ~W~I int i,j;
- attacker *temp=nasties+(*current_row * cols_of_attackers);
- static attacker_direction go_to=ATTACKER_RIGHT;
- static int maxy=0;
- boolean still_in_play=TRUE;~Y~I
-
- #BO,4,7,78,12,7,1,0,4,0,7
- Some variables are declared on the next few lines. The first two, i
- and j, are standard loop counters. These are followed by a
- declaration of a variable that is a pointer to attackers called
- "temp".
-
- #C1,R14
- ~Y~I int i,j;
- attacker *temp=nasties+(*current_row * cols_of_attackers);
- ~W~I static attacker_direction go_to=ATTACKER_RIGHT;~Y~I
- static int maxy=0;
- boolean still_in_play=TRUE;
-
- #BO,4,6,78,13,7,1,0,6,0,7
- This variable declaration is slightly unusual. This line declares the
- variable "go_to", which is of type attacker_direction, that is of
- storage class static. This means that the variable will "remember"
- its value from one invocation of the function to the next. This
- variable is used to keep track of which direction the row will move
- next, when the attackers change direction.
-
- #C1,R16
- ~Y~I static attacker_direction go_to=ATTACKER_RIGHT;
- ~W~I static int maxy=0;~Y~I
-
- #BO,4,6,78,9,7,1,0,2,0,7
- This line has a variable that keeps track of the maximum number of
- scan lines on the screen.
-
- #C1,R17
- ~Y~I static int maxy=0;
- ~W~I boolean still_in_play=TRUE;~Y~I
-
- #BO,4,6,78,10,7,1,0,3,0,7
- The variable on this line tells us whether or not the row has moved
- out of bounds. This is the value that will be returned by the
- function.
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 48
- #HS,1,4,80,25,11,1
- #C1,R5
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
-
- ~W~I switch(get_attacker_direction(nasties))
- ~Y~I {
- case ATTACKER_LEFT:
- temp=nasties+(*current_row * cols_of_attackers);
- for (i=0;i<cols_of_attackers;i++)
- {
- if (get_attacker_is_alive(temp))
- {
- erase_an_attacker(temp);
-
- #C1,R23
- ~C~IContinued On Next Page~Y~I
-
- #BO,4,17,78,22,7,1,0,4,0,7
- The real code for this function starts with a switch statement. The
- value that is switched on is the current direction of the attacker.
- There are cases for all of the valid directions in which an attacker
- can move. Let's look at each case in turn.
-
- #C1,R8
- ~Y~I switch(get_attacker_direction(nasties))
-
- #C1,R11
- ~W~Itemp=nasties+(*current_row * cols_of_attackers);~Y~I
-
- #BO,4,17,78,21,7,1,0,3,0,7
- If the current direction that the attacker is moving in is left, the
- first thing we do is set a pointer to the beginning of the current
- row.
-
- #C1,R11
- temp=nasties+(~W~I*~Y~Icurrent_row * cols_of_attackers);
-
- #BO,4,17,78,21,7,1,0,3,0,7
- Don't be confused by the extra * in front of current_row. The * before
- the variable current_row does something different than the one between
- current_row and cols_of_attackers.
-
- #C1,R11
- temp=nasties+(~W~I*current_row ~Y~I* cols_of_attackers);
-
- #BO,4,17,78,22,7,1,0,4,0,7
- The * before the variable current_row dereferences the pointer to the
- integer that contains the current row number. We read this as "the
- contents of what the pointer current_row points at." The * between
- current_row and cols_of_attackers is a multiplication.
-
- #C1,R11
- ~Y~I temp=nasties+(*current_row * cols_of_attackers);
- ~W~I for (i=0;i<cols_of_attackers;i++)~Y~I
-
- #BO,4,17,78,19,7,1,0,1,0,7
- The temp pointer is then moved down the row in the for loop.
-
- #C1,R12
- ~Y~I for (i=0;i<cols_of_attackers;i++)
- {
- ~W~I if (get_attacker_is_alive(temp))
- {
- erase_an_attacker(temp);~Y~I
-
- #BO,4,17,78,21,7,1,0,3,0,7
- When each attacker in the row is pointed at by temp, it is asked if it
- is alive. If it is, its bitmap is erased from its current position on
- the screen.
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 49
- #HS,1,4,80,25,11,1
- #C1,R4
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
-
- ~W~I if (get_attacker_col(temp) -
- get_attacker_move_increment(temp) <= 0)
- {
- set_all_attacker_direction(nasties,
- rows_of_attackers,
- cols_of_attackers,
- ATTACKER_DOWN);
- ~Y~I go_to=ATTACKER_RIGHT;
- i=cols_of_attackers;
- *current_row=rows_of_attackers;
- }
- else
- set_attacker_col(temp,
- get_attacker_col(temp) -
- get_attacker_move_increment(temp));
-
- #C1,R23
- ~C~IContinued On Next Page~Y~I
-
- #BO,4,17,78,22,7,1,0,4,0,7
- If the attacker being pointed at is about to move off the left edge
- of the screen, the direction of all of the attackers in the row is
- set so that they will move downward the next time this function is
- called.
-
- #C1,R4
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
-
- ~Y~I if (get_attacker_col(temp) -
- get_attacker_move_increment(temp) <= 0)
- {
- set_all_attacker_direction(nasties,
- rows_of_attackers,
- cols_of_attackers,
- ATTACKER_DOWN);
- ~W~I go_to=ATTACKER_RIGHT;
- i=cols_of_attackers;~Y~I
-
- #BO,4,17,78,21,7,1,0,3,0,7
- The variable go_to is set so that after the row is moved down, it
- will move to the right. The variable i is given the value of
- cols_of_attackers so that the for loop will terminate.
-
- #C1,R14
- ~Y~I go_to=ATTACKER_RIGHT;
- i=cols_of_attackers;
- ~W~I *current_row=rows_of_attackers;~Y~I
-
- #BO,4,7,78,11,7,1,0,3,0,7
- Finally, the contents of the pointer current_row is assigned the
- value of the last row in the array. The reason for this will become
- clear when we discuss the case of attackers moving down.
-
- #C1,R16
- ~Y~I *current_row=rows_of_attackers;
- }
- ~W~I else
- set_attacker_col(temp,
- get_attacker_col(temp) -
- get_attacker_move_increment(temp));
- ~Y~I
-
- #BO,4,7,78,12,7,1,0,4,0,7
- If the attacker has not moved off the left edge of the screen, the
- column number of the new position of the upper left corner of the
- attacker's bitmap is calculated and assigned to the appropriate
- field in the attacker structure.
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 50
- #HS,1,4,80,25,11,1
- #C1,R5
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
-
- ~W~I draw_an_attacker(temp,COPY_PUT);
- }
- temp++;~Y~I
- }
- break;
-
- #C1,R23
- ~C~IContinued On Next Page~Y~I
-
- #BO,4,17,78,21,7,1,0,3,0,7
- The attacker is then drawn at the new position. At the end of the
- for loop, the temp pointer is incremented to point at the next
- attacker in the row currently being moved.
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 51
- #HS,1,4,80,25,11,1
- #C1,R5
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
-
- case ATTACKER_RIGHT:
- ~W~I temp=nasties + (*current_row * cols_of_attackers) +
- (cols_of_attackers-1);~Y~I
-
- for (i=cols_of_attackers-1;i>=0;i--)
- {
- if (get_attacker_is_alive(temp))
- {
- erase_an_attacker(temp);
-
- #C1,R23
- ~C~IContinued On Next Page~Y~I
-
- #BO,4,17,78,20,7,1,0,2,0,7
- The logic for moving right is very similar to the logic for moving an
- attacker left. We set temp to point at the end of the current row.
-
- #C1,R5
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
-
- case ATTACKER_RIGHT:
- temp=nasties + (*current_row * cols_of_attackers) +
- (cols_of_attackers-1);
-
- ~W~I for (i=cols_of_attackers-1;i>=0;i--)
- {
- if (get_attacker_is_alive(temp))
- {
- erase_an_attacker(temp);~Y~I
-
- #BO,4,17,78,21,7,1,0,3,0,7
- Next we go into a for loop, stepping along the row, erasing each
- attacker, and then testing it to see if it is about to move off the
- right edge of the screen. The test is shown on the next page.
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 52
- #HS,1,4,80,25,11,1
- #C1,R4
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
-
- ~W~I if (get_attacker_col(temp) +
- get_attacker_move_increment(temp) +
- get_attacker_bitmap_width(temp) >=
- getmaxx())
- {
- set_all_attacker_direction(nasties,
- rows_of_attackers,
- cols_of_attackers,
- ATTACKER_DOWN);
- ~Y~I go_to=ATTACKER_LEFT;
- i=0;
- *current_row=rows_of_attackers;
- }
- else
- set_attacker_col(temp,get_attacker_col(temp) +
- get_attacker_move_increment(temp));
-
- #C1,R23
- ~C~IContinued On Next Page~Y~I
-
- #BO,4,17,78,20,7,1,0,2,0,7
- If the attacker is about to move off the screen, the direction of all
- of the attackers in the row is set to ATTACKER_DOWN.
-
- #C1,R4
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
-
- ~Y~I if (get_attacker_col(temp) +
- get_attacker_move_increment(temp) +
- get_attacker_bitmap_width(temp) >=
- getmaxx())
- {
- set_all_attacker_direction(nasties,
- rows_of_attackers,
- cols_of_attackers,
- ATTACKER_DOWN);
- ~W~I go_to=ATTACKER_LEFT;~Y~I
-
- #BO,4,7,78,10,7,1,0,2,0,7
- The variable go_to is set to ATTACKER_LEFT so that the attackers will
- move left after they have moved down.
-
- #C1,R16
- ~Y~I go_to=ATTACKER_LEFT;
- ~W~I i=0;
- *current_row=rows_of_attackers;~Y~I
-
- #BO,4,7,78,11,7,1,0,3,0,7
- The loop counter i is set to 0 so that the loop will terminate, and
- the contents of the pointer current_row is assigned the value of the
- last row in the array.
-
- #C1,R17
- ~Y~I i=0;
- *current_row=rows_of_attackers;
- }
- ~W~I else
- set_attacker_col(temp,get_attacker_col(temp) +
- get_attacker_move_increment(temp));
- ~Y~I
-
- #BO,4,7,78,10,7,1,0,2,0,7
- If the attacker hasn't reached the right edge of the screen, the
- column number of its upper left corner is adjusted to the right.
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 53
- #HS,1,4,80,25,11,1
- #C1,R5
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
- ~W~I
- draw_an_attacker(temp,COPY_PUT);
- }
- temp--;
- ~Y~I }
- break;
-
- #C1,R23
- ~C~IContinued On Next Page~Y~I
- #BO,4,17,78,20,7,1,0,2,0,7
- The current attacker is then drawn, and the temp pointer is
- decremented to point at the next attacker.
-
- #BO,4,16,78,23,7,1,0,6,0,7
- When moving an attacker, we've always got to move the ones on the
- leading edge of the array first. So when we move the attackers left,
- we start with the leftmost attackers and work toward the right. When
- moving them right, we start with the rightmost attackers and work to
- the left. When moving down, we start with the lowest attackers and
- work upward.
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 54
- #HS,1,4,80,25,11,1
- #C1,R4
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
-
-
- case ATTACKER_DOWN:
- ~W~I temp=nasties+(*current_row * cols_of_attackers);
-
- ~Y~I for (i=0;i<cols_of_attackers;i++)
- {
- if (get_attacker_is_alive(temp))
- {
- erase_an_attacker(temp);
- set_attacker_row(temp,
- get_attacker_row(temp) +
- get_attacker_move_increment(temp));
- draw_an_attacker(temp,COPY_PUT);
- }
- temp++;
- }
-
- #C1,R23
- ~C~IContinued On Next Page~Y~I
-
- #BO,4,17,78,21,7,1,0,3,0,7
- Moving downward is a very different task than moving left or right.
- First, we set a pointer to point at the beginning of the row being
- moved.
-
- #C1,R4
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
-
-
- case ATTACKER_DOWN:
- temp=nasties+(*current_row * cols_of_attackers);
-
- ~W~I for (i=0;i<cols_of_attackers;i++)
- {
- if (get_attacker_is_alive(temp))
- {
- erase_an_attacker(temp);
- set_attacker_row(temp,
- get_attacker_row(temp) +
- get_attacker_move_increment(temp));
- draw_an_attacker(temp,COPY_PUT);
- }
- temp++;
- }
- ~Y~I
-
- #BO,4,5,78,9,7,1,0,3,0,7
- Then we enter a for loop and ask each attacker if it's alive. If it
- is, it is erased, and its new position is set. The attacker is then
- drawn at its new position.
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 55
- #HS,1,4,80,25,11,1
- #C1,R5
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
-
-
- ~W~I if (*current_row<=0)
- set_all_attacker_direction(nasties,
- rows_of_attackers,
- cols_of_attackers,
- go_to);
-
- ~Y~I if (maxy==0)
- maxy=getmaxy();
-
- #C1,R23
- ~C~IContinued On Next Page~Y~I
-
- #BO,4,17,78,21,7,1,0,3,0,7
- Once the current row has been moved, we test to see if the current
- row is less than or equal to zero. If it is, the direction of all of
- the attackers is set to whatever the variable go_to contains.
-
- #BO,4,17,78,21,7,1,0,3,0,7
- By the way, the actual test here only needs to see if the current row
- is equal to zero. However, I made it less than or equal to just in
- case there was some kind of error in the program. It's a safer test.
-
- #C1,R5
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
-
-
- ~Y~I if (*current_row<=0)
- set_all_attacker_direction(nasties,
- rows_of_attackers,
- cols_of_attackers,
- go_to);
-
- ~W~I if (maxy==0)
- maxy=getmaxy();
- ~Y~I
-
- #BO,4,7,78,11,7,1,0,3,0,7
- The variable maxy is set to the maximum number of lines on the screen.
- This is only done once. Because maxy is a static variable, it will
- retain its value between invocations of this function.
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 56
- #HS,1,4,80,25,11,1
- #C1,R4
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
-
- for (i=rows_of_attackers-1;(i>=0) && (still_in_play);i--)
- {
- temp=nasties+(i * cols_of_attackers);
- for (j=0;(j<cols_of_attackers) && (still_in_play);j++)
- {
- if ((get_attacker_is_alive(temp)) &&
- (get_attacker_row(temp) +
- (2 * get_attacker_bitmap_height(temp)) >= maxy))
- {
- still_in_play=FALSE;
- }
- temp++;
- }
- }
-
- break;
-
- #C1,R23
- ~C~IContinued On Next Page~Y~I
-
- #BO,4,17,78,23,7,1,0,5,0,7
- Next, a pair of nested for loops are entered. Each attacker is
- tested to see if it has gone off of the bottom of the screen, If it
- has, the variable still_in_play is set to FALSE, indicating that the
- player has lost this round. If it hasn't, still_in_play will retain
- its value of TRUE, and the game will continue.
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 57
- #HS,1,4,80,25,11,1
- #C1,R5
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
-
- }
- ~W~Ireturn(still_in_play);~Y~I
- }
-
- /*---------------------------end move_attackers-----------------------------*/
-
-
- #C1,R23
- ~C~IContinued On Next Page~Y~I
-
- #BO,4,17,78,19,7,1,0,2,0,7
- The function then terminates by returning the value still_in_play.
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 58
- #HS,1,4,80,25,11,1
- #C1,R5
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
-
-
- /*---------------------------shoot_attacker_gun-----------------------------*/
-
- ~W~Ivoid shoot_attacker_gun(attacker *nasties,int rows_of_attackers,
- int cols_of_attackers)
- ~Y~I{
- int i,j;
- attacker *temp;
- boolean fired;
- static int last_fired_row=0,last_fired_col=0;
-
- #C1,R23
- ~C~IContinued On Next Page~Y~I
-
- #BO,4,17,78,20,7,1,0,2,0,7
- Attackers will attempt to shoot at the player from time to time. This
- action is handled by the function shoot_attacker_gun().
-
- #C1,R5
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
-
-
- /*---------------------------shoot_attacker_gun-----------------------------*/
-
- ~W~Ivoid ~Y~Ishoot_attacker_gun(attacker *nasties,int rows_of_attackers,
- int cols_of_attackers)
-
- #BO,4,17,78,19,7,1,0,2,0,7
- This function does not return a value.
-
- #C1,R5
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
-
-
- /*---------------------------shoot_attacker_gun-----------------------------*/
-
- void shoot_attacker_gun(~W~Iattacker *nasties~Y~I,~W~Iint rows_of_attackers~Y~I,
- ~W~Iint cols_of_attackers~Y~I)
-
- #BO,4,17,78,21,7,1,0,3,0,7
- The only parameters it needs are a pointer to the array of attackers,
- the number of rows in the array, and the number of columns in the
- array.
-
- #C1,R5
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
-
-
- /*---------------------------shoot_attacker_gun-----------------------------*/
-
- void shoot_attacker_gun(attacker *nasties,int rows_of_attackers,
- int cols_of_attackers)
-
- #BO,4,17,78,23,7,1,0,5,0,7
- This function is rather unusual in that it must not only fire the
- bullet of one of the attackers, it must keep track of which attacker
- was the last to fire. We don't want one or two attackers to be the
- only ones that are shooting at the player. It would be nice if there
- was a fairly even firing distribution.
-
- #BO,4,14,78,23,7,1,0,8,0,7
- We can achieve this by cycling through the array of attackers. The
- first time we find an attacker that is at the bottom of its row and
- doesn't currently have a bullet in flight, we tell it to shoot. We
- then have to keep track of which attacker was the last to shoot. The
- next time this function is called, we'll start cycling through the
- array with the attacker that is immediately after the one that last
- shot its bullet. The logic of shoot_attacker_gun() follows this
- pattern.
-
- #C1,R14
- ~W~I int i,j;
- attacker *temp;
- boolean fired;
- static int last_fired_row=0,last_fired_col=0;~Y~I
-
- #BO,4,17,78,19,7,1,0,2,0,7
- As always, we start the function by declaring all local variables.
-
- #C1,R14
- ~Y~I int i,j;
- attacker *temp;
- boolean fired;
- ~W~I static int last_fired_row=0,last_fired_col=0;~Y~I
-
- #BO,4,7,78,12,7,1,0,4,0,7
- The only thing significant in this part of the function is that the
- variables last_fired_row and last_fired_col are declared as static.
- These variables will be used to remember which attacker fired last,
- that's why they need to be static.
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 59
- #HS,1,4,80,25,11,1
- #C1,R5
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
-
- ~W~I if (last_fired_col>=cols_of_attackers)
- {
- last_fired_col=0;
- last_fired_row++;
- }
- ~Y~I if (last_fired_row>=rows_of_attackers)
- last_fired_row=0;
-
- for (i=last_fired_row,fired=FALSE;(i<rows_of_attackers) && (!fired);i++)
- {
- for (j=last_fired_col;(j<cols_of_attackers) && (!fired);j++)
- {
- temp=nasties+(i*cols_of_attackers)+j;
-
- #C1,R23
- ~C~IContinued On Next Page~Y~I
-
-
-
- #BO,4,17,78,21,7,1,0,3,0,7
- The first thing that happens in the function is to check to see if
- the variable last_fired_col was incremented beyond the last column.
- If so, it is reset to column zero.
-
- #C1,R8
- ~Y~I if (last_fired_col>=cols_of_attackers)
- {
- last_fired_col=0;
- last_fired_row++;
- }
- ~W~I if (last_fired_row>=rows_of_attackers)
- last_fired_row=0;
-
- #BO,4,17,78,21,7,1,0,3,0,7
- Next, we test to see if last_fired_row has been incremented beyond
- the number of rows in the array. If that's the case, we start over
- again at the first row.
-
- #C1,R13
- ~Y~I if (last_fired_row>=rows_of_attackers)
- last_fired_row=0;
- ~W~I
- for (i=last_fired_row,fired=FALSE;(i<rows_of_attackers) && (!fired);i++)
- {
- for (j=last_fired_col;(j<cols_of_attackers) && (!fired);j++)
- ~Y~I
-
- #BO,4,7,78,12,7,1,0,4,0,7
- The function then goes into a pair of nested for loops that move
- through the array and ask each attacker three things: 1) are you
- alive, 2) are you the in a position to shoot, and 3) is your bullet
- currently not in flight.
-
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 60
- #HS,1,4,80,25,11,1
- #C1,R5
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
-
- if ((get_attacker_is_alive(temp)) &&
- (get_attacker_at_edge(temp)) &&
- (!get_attacker_has_bullet(temp)))
- {
- ~W~I phew();
-
- set_attacker_bullet_row(temp,
- get_attacker_row(temp) +
- get_attacker_bitmap_height(temp) + 3);
- ~Y~I
- set_attacker_bullet_col(temp,
- get_attacker_col(temp) +
- (get_attacker_bitmap_width(temp)/2));
-
- #C1,R23
- ~C~IContinued On Next Page~Y~I
-
-
- #BO,4,17,78,20,7,1,0,2,0,7
- If these three conditions are met, then a noise is made with a call
- to the function phew(). The position of the attacker's bullet is set.
-
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 61
- #HS,1,4,80,25,11,1
- #C1,R5
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
-
- ~W~I set_attacker_bullet(temp,TRUE);
- set_attacker_bullet_direction(temp,BULLET_DOWN);
- set_attacker_bullet_color(temp,COLOR1);
- draw_bullet(&get_attacker_bullet(temp));
- ~Y~I last_fired_row=i;
- last_fired_col=j+1;
- fired=TRUE;
- }
- }
- }
- }
-
- /*-------------------------end shoot_attacker_gun---------------------------*/
-
-
- #C1,R23
- ~C~IContinued On Next Page~Y~I
-
-
- #BO,4,17,78,20,7,1,0,2,0,7
- The attacker is told that is has a bullet in flight. The bullet's color
- and direction are set and the bullet is drawn.
-
- #C1,R5
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
-
- ~Y~I set_attacker_bullet(temp,TRUE);
- set_attacker_bullet_direction(temp,BULLET_DOWN);
- set_attacker_bullet_color(temp,COLOR1);
- draw_bullet(&get_attacker_bullet(temp));
- ~W~I last_fired_row=i;
- last_fired_col=j+1;
- ~Y~I
-
-
- #BO,4,17,78,20,7,1,0,2,0,7
- The row and column number of the next attacker that might be able to
- fire are stored in last_fired_row and last_fired_col.
-
- #C1,R12
- ~Y~I last_fired_row=i;
- last_fired_col=j+1;
- ~W~I fired=TRUE;
-
- #BO,4,17,78,19,7,1,0,1,0,7
- The variable fired is set to TRUE so that the loops will terminate.
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 62
- #HS,1,4,80,25,11,1
- #C1,R5
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
-
- /*----------------------------attackers_all_dead----------------------------*/
- /*
- ~W~IWARNING: This function has multiple exit points.~Y~I
- */
-
- boolean attackers_all_dead(attacker *thugs,int rows_of_attackers,
- int cols_of_attackers)
- {
- int i,j;
- attacker *temp=thugs;
-
- #C1,R23
- ~C~IContinued On Next Page~Y~I
-
- #BO,4,17,78,21,7,1,0,3,0,7
- Back in Chapter 5, I mentioned that we would use structured
- programming techniques to develop the games for this book. So far, we
- have followed those tenants almost religiously.
-
- #BO,4,17,78,20,7,1,0,2,0,7
- In the function attackers_all_dead(), I've deviated from that in one
- important respect. This function has multiple exit points.
-
- #BO,4,17,78,21,7,1,0,3,0,7
- GASP! Yes, it's true. Normally, it's extremely unwise to have more
- than one entry or exit point from a given module of code. There is
- only one reason I didn't stick to that for this function - speed.
-
- #BO,4,17,78,22,7,1,0,4,0,7
- After developing Space Attackers, I used Turbo Profiler, a Borland
- product that gives information about program execution, to see where
- in the code the game was spending most of its time and which
- functions were called the most.
-
- #BO,4,16,78,23,7,1,0,6,0,7
- I found that attackers_all_dead() was one of the most frequently
- called functions in the program, and that it needed to be very, very
- efficient to get good performance out of the game. As a result, I went
- back and modified it so that it could return from more than one spot.
- Having multiple exits improved the performance of this function
- dramatically. Therefore, I left it as is.
-
- #BO,4,17,78,22,7,1,0,4,0,7
- Notice, however, that I have documented the fact that this function has
- multiple exits. There is a clear warning at the beginning of the
- function, and we'll see another at the second exit point (which is
- shown on the next page).
-
- #C1,R5
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
-
- /*----------------------------attackers_all_dead----------------------------*/
- /*
- ~Y~IWARNING: This function has multiple exit points.
-
- #C1,R13
- ~W~Iboolean ~Y~Iattackers_all_dead(attacker *thugs,int rows_of_attackers,
- int cols_of_attackers)
- #BO,4,17,78,20,7,1,0,2,0,7
- Other than that, this function is not unusual or complicated. It
- returns TRUE if the attackers are indeed all dead, and FALSE if not.
-
- #C1,R13
- ~Y~Iboolean attackers_all_dead(~W~Iattacker *thugs~Y~I,~W~Iint rows_of_attackers~Y~I,
- ~W~Iint cols_of_attackers~Y~I)
-
- #BO,4,17,78,20,7,1,0,2,0,7
- There are only three parameters, a pointer to the array of attackers,
- the number of rows in the array, and the number of columns.
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 63
- #HS,1,4,80,25,11,1
- #C1,R5
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
-
- for (i=0;i<rows_of_attackers;i++)
- {
- for (j=0;j<cols_of_attackers;j++)
- {
- if (get_attacker_is_alive(temp))
- return(FALSE); /* EXIT POINT */
- temp++;
- }
- }
- return(TRUE);
- }
-
- /*--------------------------end attackers_all_dead--------------------------*/
-
- #C1,R23
- ~C~IContinued On Next Page~Y~I
-
- #BO,4,17,78,21,7,1,0,3,0,7
- Two nested for loops are entered in which each attacker is asked if it
- is alive. As soon as one says yes, the function returns FALSE. If the
- loops execute to completion, TRUE is returned.
-
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 64
- #HS,1,4,80,25,11,1
- #C1,R5
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
-
- /*------------------------set_all_attacker_direction------------------------*/
-
- static ~W~Ivoid ~Y~Iset_all_attacker_direction(attacker *thugs,int rows_of_attackers,
- int cols_of_attackers,
- attacker_direction dir)
- {
- int i,j;
- attacker *temp=thugs;
-
- #C1,R23
- ~C~IContinued On Next Page~Y~I
-
- #BO,4,17,78,20,7,1,0,2,0,7
- The function set_all_attacker_direction() is logically very similar
- to attackers_all_dead(). There is no value returned.
-
- #C1,R10
- ~W~Istatic ~Y~Ivoid set_all_attacker_direction(attacker *thugs,int rows_of_attackers,
-
- #BO,4,17,78,23,7,1,0,5,0,7
- The only additional thing that is at all worth discussing about this
- function is the fact that it is of storage class static. Of course,
- that means that this function can only be called by other functions
- in ATTACKER.C. As previously mentioned, this is done for
- implementation hiding.
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 65
- #HS,1,4,80,25,11,1
- #C1,R5
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
-
- for (i=0;i<rows_of_attackers;i++)
- {
- for (j=0;j<cols_of_attackers;j++)
- {
- set_attacker_direction(temp,dir);
- temp++;
- }
- }
- }
-
- /*----------------------end set_all_attacker_direction----------------------*/
-
- #C1,R23
- ~C~IContinued On Next Page~Y~I
-
-
- #BO,4,17,78,22,7,1,0,4,0,7
- The function moves through the array of attackers in exactly the
- same way that attackers_all_dead() did. Instead of asking each
- attacker if it is alive, it tells each attacker what direction to
- move next.
-
- #WP
- %
- #EF
- #T15,1,Chapter 8 Implementing Space Attackers Part I Pg. 66
- #HS,1,4,80,25,11,1
- #C1,R5
- ~W~IListing 8.6 (cont)~Y~I
- ATTACKER.C
-
- /*--------------------------erase_an_attacker-------------------------------*/
-
- void erase_an_attacker(attacker *thug)
- {
-
- setfillstyle(EMPTY_FILL,0);
- bar(get_attacker_col(thug),get_attacker_row(thug),
- get_attacker_col(thug)+get_attacker_bitmap_width(thug),
- get_attacker_row(thug)+get_attacker_bitmap_height(thug));
-
- }
-
- /*------------------------end erase_an_attacker-----------------------------*/
- /*---------------------------end attacker.c---------------------------------*/
-
- #BO,4,17,78,20,7,1,0,2,0,7
- The last function in ATTACKER.C is erase_an_attacker(). It uses the
- BGI to draw a box of blank pixels over the image of an attacker.
-
- #C1,R13
- ~W~I setfillstyle(EMPTY_FILL,0);~Y~I
-
- #BO,4,17,78,20,7,1,0,2,0,7
- The function setfillstyle() is called to tell the BGI to do an empty
- fill. That is, fill with the background color.
-
- #C1,R13
- ~Y~I setfillstyle(EMPTY_FILL,0);
- ~W~I bar(get_attacker_col(thug),get_attacker_row(thug),
- get_attacker_col(thug)+get_attacker_bitmap_width(thug),
- get_attacker_row(thug)+get_attacker_bitmap_height(thug));~Y~I
-
- #BO,4,17,78,21,7,1,0,3,0,7
- The bar() function is called to draw a rectangle at the location on
- the screen of the attacker's bitmap. The rectangle is the same height
- and width as the bitmap, so it covers the image completely.
- #WP
- #X